{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from scipy.fft import fft\n",
    "from sklearn.feature_selection import SelectKBest, f_classif\n",
    "from sklearn.model_selection import train_test_split, GridSearchCV\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "from imblearn.over_sampling import SMOTE\n",
    "from sklearn.metrics import classification_report, confusion_matrix, accuracy_score, precision_score, recall_score, f1_score,balanced_accuracy_score\n",
    "from imblearn.metrics import geometric_mean_score  # 从 imblearn 导入几何均值\n",
    "from sklearn.inspection import permutation_importance\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 设置全局字体，避免中文乱码\n",
    "plt.rcParams['figure.dpi'] = 300  # 提高所有图形的分辨率\n",
    "config = {\n",
    "    \"font.family\": 'serif',\n",
    "    \"font.size\": 16,\n",
    "    \"mathtext.fontset\": 'stix',\n",
    "    \"font.serif\": ['SimSun'],  # 使用宋体，前提是系统安装了该字体\n",
    "}\n",
    "plt.rcParams.update(config)  # 更新配置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "#  数据导入\n",
    "data = pd.read_excel('附件一（训练集）.xlsx')\n",
    "data = data.drop(index=0)  # 删除第一行\n",
    "trainData = data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#  提取特征\n",
    "def extract_features(data):\n",
    "    # 提取基础特征\n",
    "    temp = data.iloc[:, 0].values  # 温度\n",
    "    freq = data.iloc[:, 1].values  # 频率\n",
    "    core_loss = data.iloc[:, 2].values  # 磁芯损耗\n",
    "\n",
    "    # 提取磁通密度数据\n",
    "    flux_density = data.iloc[:, 4:].values  # 第5到1029列\n",
    "\n",
    "    # 统计特征\n",
    "    mean_flux = np.mean(flux_density, axis=1)  # 均值\n",
    "    std_flux = np.std(flux_density, axis=1)  # 标准差\n",
    "    max_flux = np.max(flux_density, axis=1)  # 最大值\n",
    "    min_flux = np.min(flux_density, axis=1)  # 最小值\n",
    "    peak_to_peak = max_flux - min_flux  # 峰峰值\n",
    "    skewness_flux = skew(flux_density, axis=1)  # 偏度\n",
    "    kurtosis_flux = kurtosis(flux_density, axis=1)  # 峰度\n",
    "\n",
    "    # 傅里叶变换特征\n",
    "    fft_flux = np.abs(fft(flux_density, axis=1))\n",
    "    fft_mean = np.mean(fft_flux[:, 1:], axis=1)  # 排除直流分量后的均值\n",
    "    fft_std = np.std(fft_flux[:, 1:], axis=1)  # 排除直流分量后的标准差\n",
    "\n",
    "    # 组合所有特征\n",
    "    features = np.column_stack([temp, freq, core_loss, mean_flux, std_flux, max_flux, min_flux,\n",
    "                                peak_to_peak, skewness_flux, kurtosis_flux, fft_mean, fft_std])\n",
    "    return features\n",
    "\n",
    "X = extract_features(trainData)\n",
    "y = pd.factorize(trainData.iloc[:, 3])[0]  # 将波形类型转换为数字"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 划分训练集和验证集\n",
    "X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义多个模型和对应的参数网格\n",
    "models = {\n",
    "    'SVM': SVC(),\n",
    "    'Logistic Regression': LogisticRegression(max_iter=1000, random_state=42),\n",
    "    'KNN': KNeighborsClassifier(),\n",
    "    'Random Forest': RandomForestClassifier(random_state=42)\n",
    "}\n",
    "\n",
    "# 定义每个模型的参数网格\n",
    "param_grids = {\n",
    "    'SVM': {\n",
    "        'C': [0.1, 1, 10, 100],\n",
    "        'gamma': ['scale', 'auto', 0.001, 0.01, 0.1, 1]\n",
    "    },\n",
    "    'Logistic Regression': {\n",
    "        'penalty': ['l2'],\n",
    "        'C': [0.1, 1, 10, 100],\n",
    "        'solver': ['lbfgs', 'saga']\n",
    "    },\n",
    "    'KNN': {\n",
    "        'n_neighbors': [3, 5, 7, 9],\n",
    "        'weights': ['uniform', 'distance'],\n",
    "        'metric': ['euclidean', 'manhattan']\n",
    "    },\n",
    "    'Random Forest': {\n",
    "        'n_estimators': [50, 100, 200],\n",
    "        'max_depth': [None, 10, 20, 30],\n",
    "        'min_samples_split': [2, 5, 10]\n",
    "    }\n",
    "}\n",
    "\n",
    "# 存储最佳模型的字典\n",
    "best_models = {}\n",
    "metrics_results = []\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用网格搜索优化每个模型\n",
    "for model_name, model in models.items():\n",
    "    print(f\"正在优化 {model_name} 模型...\")\n",
    "    param_grid = param_grids[model_name]\n",
    "    grid_search = GridSearchCV(model, param_grid, cv=5, scoring='accuracy', n_jobs=-1)\n",
    "    grid_search.fit(X_train, y_train)\n",
    "    best_models[model_name] = grid_search.best_estimator_\n",
    "    print(f\"{model_name} 的最佳参数: {grid_search.best_params_}\")\n",
    "    print(f\"{model_name} 的最佳评分: {grid_search.best_score_:.4f}\\n\")\n",
    "\n",
    "# 使用最佳模型在验证集上进行预测\n",
    "for model_name, model in best_models.items():\n",
    "    print(f\"使用 {model_name} 模型进行验证集预测...\")\n",
    "    y_val_pred = model.predict(X_val)\n",
    "    accuracy = accuracy_score(y_val, y_val_pred)\n",
    "    precision = precision_score(y_val, y_val_pred, average='weighted')\n",
    "    recall = recall_score(y_val, y_val_pred, average='weighted')\n",
    "    f1 = f1_score(y_val, y_val_pred, average='weighted')\n",
    "    g_mean = geometric_mean_score(y_val, y_val_pred, average='weighted')\n",
    "    \n",
    "    # 保存评估结果\n",
    "    metrics_results.append([accuracy, precision, recall, f1, g_mean])\n",
    "\n",
    "    # 输出评估结果\n",
    "    print(f\"{model_name} 模型的验证集评估结果：\")\n",
    "    print(f\"准确率: {accuracy:.4f}\")\n",
    "    print(f\"精确率: {precision:.4f}\")\n",
    "    print(f\"召回率: {recall:.4f}\")\n",
    "    print(f\"F1得分: {f1:.4f}\")\n",
    "    print(f\"G-Mean: {g_mean:.4f}\\n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 计算混淆矩阵\n",
    "cm = confusion_matrix(y_val, y_val_pred)\n",
    "\n",
    "# 绘制混淆矩阵\n",
    "plt.figure(figsize=(8, 6))\n",
    "sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=True, linewidths=1, linecolor='black')\n",
    "\n",
    "# 设置标题和轴标签\n",
    "plt.title(f\"{best_model_name} 模型的验证集混淆矩阵\", fontsize=16)\n",
    "plt.xlabel(\"预测标签\", fontsize=14)\n",
    "plt.ylabel(\"真实标签\", fontsize=14)\n",
    "\n",
    "# 根据实际类别名称替换标签\n",
    "labels = ['类别1', '类别2', '类别3']  # 请根据实际的类别名称进行替换\n",
    "ax = plt.gca()\n",
    "ax.set_xticks(np.arange(len(labels)) + 0.5)\n",
    "ax.set_yticks(np.arange(len(labels)) + 0.5)\n",
    "ax.set_xticklabels(labels, fontsize=12)\n",
    "ax.set_yticklabels(labels, fontsize=12)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import seaborn as sns\n",
    "\n",
    "# 获取模型预测的类别概率\n",
    "y_val_pred_prob = best_model.predict_proba(X_val)\n",
    "\n",
    "# 选择前5个样本，绘制它们的概率分布\n",
    "plt.figure(figsize=(10, 6))\n",
    "for i in range(5):  # 选择前5个样本进行展示\n",
    "    sns.barplot(x=labels, y=y_val_pred_prob[i], alpha=0.7)\n",
    "    plt.title(f\"Sample {i+1} Probability Distribution\", fontsize=16)\n",
    "    plt.ylabel(\"Probability\", fontsize=14)\n",
    "    plt.xlabel(\"Class\", fontsize=14)\n",
    "    plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义metrics_df\n",
    "metrics_df = pd.DataFrame(metrics_results, columns=['Accuracy', 'Precision', 'Recall', 'F1 Score', 'G-Mean'], \n",
    "                          index=best_models.keys())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 打印列名以确认\n",
    "print(\"DataFrame 列名：\", metrics_df.columns.tolist())\n",
    "print(\"各模型性能评估：\\n\", metrics_df)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 雷达图绘制部分\n",
    "num_metrics = len(metrics_df.columns)  # 获取指标数量\n",
    "angles = np.linspace(0, 2 * np.pi, num_metrics, endpoint=False).tolist()\n",
    "\n",
    "# 为闭合添加第一个值\n",
    "angles += angles[:1]  # 将第一个角度添加到末尾以闭合\n",
    "\n",
    "# 绘制雷达图\n",
    "fig, ax = plt.subplots(figsize=(7, 7), subplot_kw=dict(polar=True))\n",
    "\n",
    "# 将雷达图刻度线设置为多边形\n",
    "ax.set_theta_offset(np.pi / 2)  # 使第一个坐标点位于顶部\n",
    "ax.set_theta_direction(-1)  # 顺时针方向\n",
    "ax.set_rgrids([])  # 移除径向网格线，使图形更加多边形化\n",
    "ax.spines['polar'].set_visible(False)  # 隐藏外边框以突出多边形\n",
    "\n",
    "# 配色方案\n",
    "colors = ['#9fb8de',  '#ffb0b8', '#b5d4e2','#c9e6c9']  # 加深的蓝、绿、粉、紫\n",
    "\n",
    "# 绘制每个模型的线条和填充\n",
    "for i, (model_name, color) in enumerate(zip(metrics_df.index, colors)):\n",
    "    values = metrics_df.loc[model_name].values.flatten().tolist()\n",
    "    values += values[:1]  # 将第一个值添加到末尾以闭合多边形\n",
    "    ax.plot(angles, values, linewidth=2, linestyle='solid', color=color, marker='o', markersize=8, label=model_name)  # 绘制线\n",
    "    ax.fill(angles, values, color=color, alpha=0.25)  # 填充区域\n",
    "\n",
    "# 设置雷达图的标签\n",
    "ax.set_yticklabels([])  # 去掉极轴标签\n",
    "ax.set_xticks(angles[:-1])  # 设置角度标签\n",
    "ax.set_xticklabels(metrics_df.columns, fontsize=18)  # 设置指标名称\n",
    "\n",
    "# 添加图例，确保每个模型对应的颜色标注清楚\n",
    "plt.legend(loc='upper right', bbox_to_anchor=(1.2, 1.1), fontsize=15)\n",
    "\n",
    "# 显示图形\n",
    "# plt.title('模型性能评估雷达图', fontsize=17)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#  6. 选择最佳模型用于附件二的测试数据\n",
    "# 假设这里选择验证集表现最好的模型\n",
    "best_model_name = max(best_models, key=lambda name: accuracy_score(y_val, best_models[name].predict(X_val)))\n",
    "best_model = best_models[best_model_name]\n",
    "print(f\"选择 {best_model_name} 作为最终用于测试集预测的模型\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 假设已经选出最佳模型，如：Random Forest\n",
    "best_model_name = max(best_models, key=lambda k: accuracy_score(y_val, best_models[k].predict(X_val)))\n",
    "best_model = best_models[best_model_name]\n",
    "\n",
    "print(f\"使用的最佳模型为: {best_model_name}\")\n",
    "\n",
    "# 如果最佳模型支持特征重要性（例如随机森林），则进行特征重要性绘制\n",
    "if hasattr(best_model, 'feature_importances_'):\n",
    "    importances = best_model.feature_importances_\n",
    "    feature_names = ['温度', '频率', '磁芯损耗', '均值', '标准差', '最大值', '最小值', \n",
    "                     '峰峰值', '偏度', '峰度', 'FFT均值', 'FFT标准差']  # 请根据你的特征顺序调整名称\n",
    "\n",
    "    # 绘制特征重要性条形图，使用Blues配色\n",
    "    indices = np.argsort(importances)[::-1]\n",
    "    plt.figure(figsize=(10, 6))\n",
    "    plt.title(f\"{best_model_name} 模型的特征重要性\")\n",
    "    \n",
    "    # 使用Seaborn的barplot并设置Blues调色板\n",
    "    sns.barplot(x=importances[indices], y=[feature_names[i] for i in indices], palette=\"Blues\")\n",
    "\n",
    "    plt.xlabel('重要性得分', fontsize=14)\n",
    "    plt.ylabel('特征', fontsize=14)\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "else:\n",
    "    print(f\"{best_model_name} 不支持特征重要性评估。\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 7. 对附件二中的样本进行分类\n",
    "testData = pd.read_excel('附件二（测试集）.xlsx')\n",
    "X_test = extract_features(testData)\n",
    "\n",
    "# 8. 使用最佳模型进行预测\n",
    "predictions = best_model.predict(X_test)\n",
    "\n",
    "# 显示预测结果\n",
    "class_names = ['正弦波', '三角波', '梯形波']\n",
    "predicted_classes = [class_names[p] for p in predictions]\n",
    "print('Predicted Waveforms:')\n",
    "print(predicted_classes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 统计每种波形的数量\n",
    "num_sine = np.sum(predictions == 0)\n",
    "num_triangle = np.sum(predictions == 1)\n",
    "num_trapezoidal = np.sum(predictions == 2)\n",
    "print(f'正弦波数量: {num_sine}')\n",
    "print(f'三角波数量: {num_triangle}')\n",
    "print(f'梯形波数量: {num_trapezoidal}')\n",
    "\n",
    "# 将波形名称映射为数字\n",
    "wave_mapping = {'正弦波': 1, '三角波': 2, '梯形波': 3}\n",
    "predicted_numbers = [wave_mapping[wave] for wave in predicted_classes]\n",
    "\n",
    "# 加载附件四 Excel 文件\n",
    "attachment_four = pd.read_excel('附件四（Excel表）.xlsx')\n",
    "\n",
    "# 根据样本序号填入相应的分类结果\n",
    "for i, prediction in enumerate(predicted_numbers):\n",
    "    # i + 1 是因为样本序号从 1 开始\n",
    "    attachment_four.loc[i, '附件二（80个样品）励磁波形分类结果'] = prediction\n",
    "\n",
    "# 保存修改后的 Excel 文件\n",
    "attachment_four.to_excel('附件四（Excel表）.xlsx', index=False)\n",
    "\n",
    "print(\"结果已按样本序号填入，并保存至 '附件四（Excel表）.xlsx' 文件中。\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 数据\n",
    "wave_types = ['正弦波(1)', '三角波(2)', '梯形波(3)']\n",
    "counts = [num_sine, num_triangle, num_trapezoidal]\n",
    "\n",
    "# 设置图形风格\n",
    "plt.figure(figsize=(8, 4))\n",
    "\n",
    "# 调整柱子宽度以减少间距\n",
    "bar_height = 0.5\n",
    "colors = ['#a6c8e0', '#f9d3b4', '#b4e1b0']\n",
    "\n",
    "# 绘制横向柱状图\n",
    "bars = plt.barh(wave_types, counts, height=bar_height, color=colors)\n",
    "\n",
    "# 在每个柱子上标出数量\n",
    "for bar, count in zip(bars, counts):\n",
    "    plt.text(bar.get_width(), bar.get_y() + bar.get_height() / 2, str(count),\n",
    "             ha='left', va='center', fontsize=14, color='black')\n",
    "\n",
    "# 设置标题和标签\n",
    "plt.title('波形数量分布', fontsize=16)\n",
    "plt.ylabel('波形类型', fontsize=14)\n",
    "plt.xlabel('数量', fontsize=14)\n",
    "\n",
    "# 显示网格线\n",
    "plt.grid(axis='x', linestyle='--', alpha=0.7)\n",
    "\n",
    "# 显示图形\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 打印指定样本序号的分类结果\n",
    "sample_indices = [1, 5, 15, 25, 35, 45, 55, 65, 75, 80]\n",
    "\n",
    "# 假设 predicted_numbers 包含按照样本顺序的分类结果数字\n",
    "for index in sample_indices:\n",
    "    # 因为索引从 0 开始，所以需要减 1\n",
    "    result = predicted_numbers[index - 1]\n",
    "    print(f\"样本序号 {index} 的分类结果: {result}\")\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py38torch",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.19"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
